package com.amar.dtadmin.apidoc.fegin;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.json.JSONUtil;
import com.amar.dtadmin.apidoc.action.DtAdminApiDocActionGroup;
import com.amar.dtadmin.apidoc.extensions.storages.DtAdminSettings;
import com.amar.dtadmin.apidoc.model.ApiDocCreateReq;
import com.amar.dtadmin.apidoc.model.ApiDocGroupReq;
import com.amar.dtadmin.apidoc.model.ApiDocGroupResp;
import com.amar.dtadmin.apidoc.model.ApiDocInfoReq;
import com.amar.dtadmin.apidoc.model.ApiDocInfoResp;
import com.amar.dtadmin.apidoc.model.ApiDocListReq;
import com.amar.dtadmin.apidoc.model.ApiDocListResp;
import com.amar.dtadmin.apidoc.model.ApiDocLockReq;
import com.amar.dtadmin.apidoc.model.ApiDocLockResp;
import com.amar.dtadmin.apidoc.model.ApiDocUpdateReq;
import com.amar.dtadmin.apidoc.model.DtAdminLoginReq;
import com.amar.dtadmin.apidoc.model.DtAdminLoginResp;
import com.amar.dtadmin.apidoc.model.DtAdminUserInfoResp;
import com.amar.dtadmin.apidoc.model.Response;
import com.amar.dtadmin.apidoc.ui.dialog.DtAdminSettingsDialog;
import com.amar.dtadmin.apidoc.util.NotificationUtils;
import com.amar.dtadmin.apidoc.util.ParamItemConvertUtil;
import com.intellij.notification.Notification;
import com.intellij.notification.NotificationAction;
import com.intellij.notification.NotificationType;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.Messages;
import feign.Feign;
import feign.FeignException;
import feign.Headers;
import feign.Param;
import feign.RequestLine;
import feign.jackson.JacksonDecoder;
import feign.jackson.JacksonEncoder;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;

/**
 * @author xiaoshiqiang
 * @since 2025/2/8
 */
@Slf4j
public class DtAdminClient {
    private final Project project;

    private final DtAdminApi dtAdminApi;

    public DtAdminClient(Project project) {
        this.project = project;
        this.dtAdminApi = DtAdminApi.getInstance();
    }

    public DtAdminLoginResp login(String username, String password) {
        try {
            return login(username, new DtAdminLoginReq(password));
        } catch (FeignException e) {
            log.error(e.getMessage());
            return null;
        }

    }

    public DtAdminLoginResp login(String loginId, DtAdminLoginReq loginParam) {
        return dtAdminApi.login(loginId, loginParam);
    }

    public Response<DtAdminUserInfoResp> userInfo(String token) {
        try {
            return dtAdminApi.userInfo(token);
        } catch (FeignException e) {
            log.error(e.getMessage());
            return null;
        }
    }

    /**
     * 上传接口文档至DtAdmin平台
     *
     * @param apiInfo 接口定义信息
     */
    public void uploadApiDoc(DtAdminApiDocActionGroup.ApiInfo apiInfo) {
        try {
            // 先查DtAdmin的配置信息，防止配置为空或者token失效
            if (checkSetting()) {
                // 1、查询api分组信息，防止api分组配置有误
                DtAdminSettings settings = DtAdminSettings.getInstance(project);
                Response<List<ApiDocGroupResp>> groups = dtAdminApi.groups(settings.getToken(), new ApiDocGroupReq(settings.getProjectId()));
                Map<String, ApiDocGroupResp> allGroup = new HashMap<>();
                groups.getData().forEach(g -> allGroup.putAll(g.flat()));
                ApiDocGroupResp apiDocGroupResp = allGroup.get(apiInfo.getGroup());
                if (apiDocGroupResp == null) {
                    Messages.showErrorDialog("未查询到API组：" + apiInfo.getGroup() + "，请检查配置", "配置错误");
                    return;
                }
                apiInfo.setGroupId(apiDocGroupResp.getGroupid());
                apiInfo.setGroupLevel(apiDocGroupResp.getGrouplevel());

                // 2、查询接口信息，接口信息不存在调用创建ApiDoc接口。接口信息存在，则调用更新ApiDoc接口
                ApiDocInfoResp apiDocInfoResp = null;
                Response<ApiDocListResp> docListRespResponse = dtAdminApi.list(settings.getToken(), new ApiDocListReq(settings.getProjectId(), apiInfo.getApi()));
                if (docListRespResponse != null && docListRespResponse.getData() != null && CollUtil.getFirst(docListRespResponse.getData().getRecords()) != null) {
                    ApiDocListResp.RecordDto record = CollUtil.getFirst(docListRespResponse.getData().getRecords());
                    ApiDocInfoReq infoReq = new ApiDocInfoReq(record.getApiid());
                    apiDocInfoResp = dtAdminApi.detail(settings.getToken(), infoReq).getData();
                }
                if (apiDocInfoResp == null) {
                    // 调用创建ApiDoc接口
                    ApiDocCreateReq createReq = new ApiDocCreateReq(settings, apiInfo);
                    String jsonPrettyStr = JSONUtil.toJsonPrettyStr(createReq);
                    System.out.println(jsonPrettyStr);
                    Response<Void> createRepose = dtAdminApi.create(settings.getToken(), createReq);
                    NotificationUtils.notifyInfo("新增接口文档[" + apiInfo.getApi() + "] -> " + createRepose.getMesg());
                } else {
                    // ApiDoc更新，对比本地生成的ApiDoc和已查得的ApiDoc，构建ApiDoc更新的接口入参
                    ApiDocUpdateReq updateReq = new ApiDocUpdateReq(settings, apiInfo);
                    updateReq.setApiid(apiDocInfoResp.getApiid());
                    updateReq.setStatus(apiDocInfoResp.getStatus());
                    if (apiDocInfoResp.getRequestparam() != null) {
                        ApiDocInfoResp.Body body = apiDocInfoResp.getRequestparam().getBody();
                        List<String> removedReqParam = ParamItemConvertUtil.compareUpdateApiDoc(updateReq.getRequestparamreq(), Optional.ofNullable(body.getJson()).orElse(body.getFormdata()), null);
                        updateReq.getParamreq().getDelreqs().addAll(removedReqParam);
                    }
                    if (apiDocInfoResp.getResponseparam() != null) {
                        ApiDocInfoResp.Body body = apiDocInfoResp.getResponseparam().getBody();
                        List<String> removedReqParam = ParamItemConvertUtil.compareUpdateApiDoc(updateReq.getResponseparamreq(), Optional.ofNullable(body.getJson()).orElse(body.getFormdata()), null);
                        updateReq.getParamreq().getDelreqs().addAll(removedReqParam);
                    }
                    updateReq.getParamreq().getModifyreqs().addAll(updateReq.getRequestparamreq());
                    updateReq.getParamreq().getModifyreqs().addAll(updateReq.getResponseparamreq());

                    // 接口更新前，先锁定接口，防止并发修改
                    Response<ApiDocLockResp> lockResp = dtAdminApi.lock(settings.getToken(), new ApiDocLockReq(apiDocInfoResp.getApiid()));
                    if (lockResp != null && lockResp.getData() != null && lockResp.getData().getIslocked()) {
                        String jsonPrettyStr = JSONUtil.toJsonPrettyStr(updateReq);
                        System.out.println(jsonPrettyStr);
                        Response<Void> response = dtAdminApi.update(settings.getToken(), updateReq);
                        NotificationUtils.notifyInfo("更新接口文档[" + apiInfo.getApi() + "] -> " + response.getMesg());
                        // 接口更新后，释放锁定接口
                        dtAdminApi.unlock(settings.getToken(), new ApiDocLockReq(apiDocInfoResp.getApiid()));
                    } else {
                        // 接口更新后，释放锁定接口
                        dtAdminApi.unlock(settings.getToken(), new ApiDocLockReq(apiDocInfoResp.getApiid()));
                        NotificationUtils.notifyWarning("更新接口文档[" + apiInfo.getApi() + "] -> 失败，稍后重试");
                    }
                }
            }
        } catch (FeignException e) {
            NotificationUtils.notifyWarning("上传接口文档[" + apiInfo.getApi() + "] -> 失败，稍后重试");
        }
    }

    private boolean checkSetting() {
        DtAdminSettings settings = DtAdminSettings.getInstance(project);
        if (BeanUtil.isEmpty(settings)) {
            NotificationUtils.notifyWithAction(NotificationType.WARNING, "DtAdmin", "未配置DtAdmin", notification -> new NotificationAction("去配置") {
                @Override
                public void actionPerformed(@NotNull AnActionEvent anActionEvent, @NotNull Notification notification) {
                    notification.expire();
                    new DtAdminSettingsDialog(anActionEvent.getProject()).show();
                }
            });
            return false;
        }
        Response<DtAdminUserInfoResp> dtAdminUserInfoRespResponse = userInfo(settings.getToken());
        if (dtAdminUserInfoRespResponse == null || dtAdminUserInfoRespResponse.getData() == null) {
            // token失效，刷新token
            DtAdminLoginResp login = login(settings.getUsername(), settings.getPassword());
            if (login == null) {
                NotificationUtils.notifyWithAction(NotificationType.WARNING, "DtAdmin", "DtAdmin账号密码配置错误，请检查", notification -> new NotificationAction("去配置") {
                    @Override
                    public void actionPerformed(@NotNull AnActionEvent anActionEvent, @NotNull Notification notification) {
                        notification.expire();
                        new DtAdminSettingsDialog(anActionEvent.getProject()).show();
                    }
                });
                return false;
            } else {
                DtAdminUserInfoResp userInfo = userInfo(login.getMessage().getAccessToken()).getData();
                settings.setToken(login.getMessage().getAccessToken());
                settings.setFullName(userInfo.getFullName());
            }
        }
        return true;
    }

    @Headers("Content-Type: application/json;charset=UTF-8")
    public interface DtAdminApi {
        static DtAdminApi getInstance() {
            return Feign.builder().encoder(new JacksonEncoder()).decoder(new JacksonDecoder()).target(DtAdminApi.class, "https://cloud.amardata.com/dt");
        }

        @RequestLine("POST /aoas/login?LoginId={loginId}&Password=jkk&code=amar&phoneNo=1")
        DtAdminLoginResp login(@Param String loginId, DtAdminLoginReq loginParam);

        @RequestLine("POST /asset/home/userInfo")
        @Headers({"token: {token}"})
        Response<DtAdminUserInfoResp> userInfo(@Param("token") String token);

        @RequestLine("PUT /asset/api/interface/add")
        @Headers({"token: {token}"})
        Response<Void> create(@Param("token") String token, ApiDocCreateReq createReq);

        @RequestLine("PUT /asset/api/interface/modify")
        @Headers({"token: {token}"})
        Response<Void> update(@Param("token") String token, ApiDocUpdateReq updateReq);


        @RequestLine("POST /asset/api/interface/list")
        @Headers({"token: {token}"})
        Response<ApiDocListResp> list(@Param("token") String token, ApiDocListReq listReq);

        @RequestLine("POST /asset/api/interface/detail")
        @Headers({"token: {token}"})
        Response<ApiDocInfoResp> detail(@Param("token") String token, ApiDocInfoReq infoReq);

        @RequestLine("POST /asset/api/group/select/all")
        @Headers({"token: {token}"})
        Response<List<ApiDocGroupResp>> groups(@Param("token") String token, ApiDocGroupReq groupReq);

        @RequestLine("POST /asset/api/common/getlock")
        @Headers({"token: {token}"})
        Response<ApiDocLockResp> lock(@Param("token") String token, ApiDocLockReq lockReq);

        @RequestLine("POST /asset/api/common/unlock")
        @Headers({"token: {token}"})
        void unlock(@Param("token") String token, ApiDocLockReq unLockReq);
    }
}
